home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Dos / VARIOS / pascal / DATETIME.SWG / 0001_Various Date Routines.pas
Encoding:
Pascal/Delphi Source File  |  1996-02-21  |  11.5 KB  |  383 lines

  1.  
  2. ======================================
  3. Datumsarithmetik mit den PC und Pascal
  4. ======================================
  5.  
  6. Dieser Informationstext soll allen Programmierern helfen, die mit
  7. der Berechnung von Tagesdaten oder der Logik der kirchlichen Feier-
  8. tage kaempfen. Diese Text kann frei verteilt werden, solange die
  9. folgenden Informationen nicht daraus entfernt werden.
  10.  
  11. Copyright (c) 1992 fuer diese Zusammenstellung und den erlaeuternden
  12. Text sowie evtl. erfolgte Korrekturen by Armin Hanisch (2:246/41)
  13. Ich moechte allen danken, die durch ihre e-Mails und sonstige Bei-
  14. traege, insbesondere der Veroeffentlichung von Sources diese Zu-
  15. sammenstellung erst moeglich gemacht haben. Nach Informationsstand
  16. des Autors haben alle Autoren diese Routinen als Public Domain frei-
  17. gegeben. Ich uebernehme allerdings weder dafuer noch fuer die korekte
  18. Berechnung irgendwelcher Daten eine Garantie. Ich habe allerdings
  19. einige Stunden an Testarbeit investiert, die Daten stimmen!
  20.  
  21. Sources und Alogrithmen von folgenden Personen wurden ausgewertet,
  22. zur Verfuegung gestellt und teilweise korrigert oder an einen fuer
  23. diesen Text einheitlichen Stil bzw. Datentyp angepasst:
  24.  
  25. Armin Hanisch - Feiertagsberechnungen
  26. Bernd Strehuber - Feiertagsberechnungen
  27. Carley Phillips - Jul. Berechnungen
  28. Jeff Duntemann - Wochentagsberechnung
  29. Judson McClendon - Osterberechnung
  30. Martin Austermeier - Tagesberechnungen
  31. Paul Schlyter - Osterberechnung
  32. Pit Biernath - Jul. Berechnungen
  33. Scott Bussinger - Jul. Berechnungen
  34.  
  35.  
  36. OSTERBERECHNUNGEN:
  37. ==================
  38.  
  39. Dieser Algorithmus basiert nicht auf der Berechnung von Gauss und
  40. kommt ohne Ausnahmen aus (lt. Paul Schlyter). Werte ueber 31 be-
  41. zeichnen den Tag im April-31, Werte darunter bezeichnen den Tag
  42. im Maerz.
  43.  
  44. FUNCTION Easter(year : INTEGER) : INTEGER;
  45. VAR  a, b, c, d, e, f, g, h, i, k, l, m : INTEGER;
  46. BEGIN
  47.    a  :=  year MOD 19;
  48.    b  :=  year DIV 100;
  49.    c  :=  year MOD 100;
  50.    d  :=  b DIV 4;
  51.    e  :=  b MOD 4;
  52.    f  :=  ( b + 8 ) DIV 25;
  53.    g  :=  ( b - f + 1 ) DIV 3;
  54.    h  :=  ( 19 * a + b - d - g + 15 ) MOD 30;
  55.    i  :=  c DIV 4;
  56.    k  :=  c MOD 4;
  57.    l  :=  ( 32 + 2 * e + 2 * i - h - k ) MOD 7;
  58.    m  :=  ( a + 11 * h + 22 * l ) DIV 451;
  59.    Easter :=  h + l - 7 * m + 22;
  60. END{FUNC};
  61.  
  62.  
  63. Eine weitere Moeglichkeit, Ostern sehr schnell zu berechnen, besteht
  64. darin, den auf das juedische Passahfest folgenden Sonntag zu berechnen.
  65.  
  66.  
  67. Der sog. Passah-Vollmond wird berechnet, in dem das Jahr durch 19 ge-
  68. teilt wird und der Rest mit der folgenden Tabelle verglichen wird:
  69.  
  70.     0: Apr 14       5: Apr 18      10: Mrz 25      15: Mrz 30
  71.     1: Apr 03       6: Apr 08      11: Apr 13      16: Apr 17
  72.     2: Mrz 23       7: Mrz 28      12: Apr 02      17: Apr 07
  73.     3: Apr 11       8: Apr 16      13: Mrz 22      18: Mrz 27
  74.     4: Mrz 31       9: Apr 05      14: Apr 10
  75.  
  76. Faellt dieses Datum auf einen Sonntag, ist Ostern der naechste Sonntag!
  77.  
  78. Beispiel: 1992 MOD 19 = 16, daraus folgt 17.04., der naechste Sonntag
  79.           ist dann der 19. April (Ostersonntag)
  80.  
  81.  
  82. FEIERTAGE:
  83. ==========
  84.  
  85. Massgebend fuer die kirchlichen Feiertage ist sowohl das Osterdatum
  86. als auch der 1. Advent, der Beginn des Krichenjahres. Wie man Ostern
  87. berechnet, wurde oben erlaeutert. Hier nun also die Berechnungen der
  88. restlichen Feiertage.
  89.  
  90. Aschermittwoch:      40 Tage vor dem Ostersonntag,
  91.                      dann zurⁿckgehen bis zum Mittwoch
  92.                      Bsp.:  result := GetOstern;
  93.                             Dec(result,40);
  94.                             WHILE DayOfWeek(result) <> 3 DO
  95.                                Dec(result);
  96.  
  97. Palmsonntag:         Der Sonntag vor dem Ostersonntag, die Berechnung
  98.                      ist damit trivial.
  99.  
  100. Weisser Sonntag:     Der Sonnrtag nach Ostern, ebenfalls simpel.
  101.  
  102. Christi Himmelfahrt: 39 Tage nach dem Ostersonntag oder anders gesagt,
  103.                      der zweite Donnerstag vor Pfingsten.
  104.  
  105. Pfingsten:           49 Tage nach dem Ostersonntag.
  106.  
  107. Fronleichnam:        60 Tage nach dem Ostersonntag.
  108.  
  109. Maria Himmelfahrt:   Fest am 15. August (nicht ueberall Feiertag!)
  110.  
  111. 1. Advent:           Vom 24.12. zurⁿck bis zum nΣchsten Sonntag,
  112.                      dann noch drei Wochen zurⁿck.
  113.                      Bsp.:  result := MakeDate(24,12,year);
  114.                             WHILE DayOfWeek(result) <> 0 DO
  115.                                Dec(result);
  116.                             Dec(result,21);
  117.  
  118. Buss- und Bettag:    Der vorvorige Mittwoch vor dem 1. Advent, also
  119.                      vom 1. Advent aus den Mittwoch suchen, dann noch
  120.                      eine Woche zurⁿck.
  121.                      Bsp:  <adventberechnung>   <-- wie oben
  122.                            WHILE DayOfWeek(result) <> 3 DO
  123.                               Dec(result);
  124.                            Dec(result,7);
  125.  
  126.  
  127. Hl. drei K÷inige:    Fest am 06.01.
  128.  
  129. Allerheiligen:       Fest am 01.11.
  130.  
  131. Tag der Arbeit:      Fest am 01.05.
  132.  
  133. Tag der dt. Einheit: Fest am 03.10. Hier wird im Zuge von Sparmassnahmen
  134.                      fⁿr die einzufⁿhrende Pflegeversicherung allerdings
  135.                      ⁿberlegt, diesen Feiertag immer auf den ersten Sonn-
  136.                      tag im Oktober zu legen, man sollte hier also die
  137.                      politischen Nachrichten verfolgen!
  138.  
  139.  
  140. DATUMSARITHMETIK:
  141. =================
  142.  
  143. Berechnung eines Schaltjahres
  144. -----------------------------
  145.  
  146. FUNCTION LeapYear(year : WORD) : BOOLEAN;
  147. BEGIN
  148.    LeapYear := ((year MOD 4 = 0) AND (year MOD 100 <> 0))
  149.                OR (year MOD 400 = 0);
  150. END;
  151.  
  152.  
  153. Berechnung des Wochentages
  154. --------------------------
  155.  
  156. FUNCTION DayOfWeek(Day,Month,Year: Integer): INTEGER;
  157. VAR century,yr,dw: Integer;
  158. BEGIN
  159.   IF Month < 3 THEN BEGIN
  160.     Inc(Month,10);
  161.     Dec(Year);
  162.   END{IF} ELSE
  163.     Dec(Month,2);
  164.   century := Year div 100;
  165.   yr := year mod 100;
  166.   dw := (((26*month-2) div 10)+day+yr+(yr div 4)
  167.         +(century div 4)-(2*century)) mod 7;
  168.   IF dw < 1 THEN Inc(dw,7);
  169.   DayOfWeek:=dw;
  170. END{FUNC};
  171.  
  172. Als Ergebnis erhaelt man den Wochentag in folgender Reiehenfolge:
  173. 0=Sonntag, 1=Montag ..... 6=Samstag
  174.  
  175.  
  176. Berechnung der Kalenderwoche
  177. ----------------------------
  178.  
  179. Die Woche 1 ist die Woche, die den ersten Donnerstag des Jahres
  180. enthaelt, also mehr als die Haelfte diesem Jahr angehoert.
  181. Ist der 01.01. ein Mo-Mi, dann liegt der 01.01. in der letzten
  182. Woche des vergangenen Jahres. (DIN 1355)
  183.  
  184. FUNCTION WeekOfYear (Day,Month,Year:WORD) : WORD;
  185. CONST
  186.   table1 : ARRAY [0..6] OF ShortInt = ( -1,  0,  1,  2,  3, -3, -2);
  187.   table2 : ARRAY [0..6] OF ShortInt = ( -4,  2,  1,  0, -1, -2, -3);
  188. VAR
  189.   doy1 ,
  190.   doy2 : INTEGER;
  191. BEGIN
  192.   doy1 := DayofYear (Day,Month,Year) + table1[DayOfWeek (1,1,Year)];
  193.   doy2 := DayofYear (Day,Month,Year) + table2[DayOfWeek(Day,Month,Year)];
  194.   IF doy1 <= 0 THEN WeekOfYear := WeekOfYear(31,12,Year-1)
  195.    ELSE IF doy2 >= DayofYear(31,12,Year) THEN WeekOfYear:=1
  196.      ELSE WeekOfYear := (doy1-1) DIV 7 + 1;
  197. END;
  198.  
  199.  
  200. Berechnung der Tage im Monat
  201. ----------------------------
  202.  
  203. FUNCTION DaysInMonth(month,year : WORD) : INTEGER;
  204. VAR ly : BOOLEAN;  { leap year? }
  205. BEGIN
  206.    ly := ((year MOD 4 = 0) AND (year MOD 100 <> 0)) OR (year MOD 400 = 0);
  207.    IF (month IN [04,06,09,11]) THEN  { even month }
  208.      DaysInMonth := 30
  209.    ELSE
  210.      IF month <> 2 THEN  { rest except february }
  211.        DaysInMonth := 31
  212.      ELSE
  213.        IF ly THEN  { leap year? }
  214.          DaysInMonth := 29
  215.        ELSE
  216.          DaysInMonth := 28;
  217. END{FUNC};
  218.  
  219.  
  220. Berechnung des Tages im Jahr
  221. ----------------------------
  222.  
  223. Diese Methode gilt fuer alle Jahre ab 1582, der Einfⁿhrung des
  224. gregorianischen Kalenders.
  225.  
  226. FUNCTION DayOfYear (day,month,year : WORD) : INTEGER;
  227. VAR
  228.   i, tage : Integer;
  229. BEGIN
  230.   tage := 0;
  231.   FOR i := 1 TO Pred(month) DO Inc (tage, DaysInMonth(i,year));
  232.      Inc (tage,day);
  233.   DayOfYear := tage;
  234. END;
  235.  
  236. Eine andere Methode kommt ohne die Berechnung der Tage im Monat aus
  237. und bezieht ebenfalls Schaltjahre ein. Der Gⁿltigkeitsbereich dieses
  238. Alogorithmus liegt von 1901 bis 2099.
  239.  
  240. FUNCTION DayNumber(Day,Month,Year : INTEGER ) : INTEGER;
  241. VAR
  242.   term1 ,
  243.   term2 ,
  244.   term3 : INTEGER;
  245. BEGIN
  246.    term1 := ( 275 * month ) div 9;
  247.    term2 := ( month + 9 ) div 12;
  248.    term3 := ( ( year mod 4 ) + 2 ) div 3;
  249.    DayNumber := term1 - term2 * ( 1 +  term3 ) + day - 30;
  250. END;
  251.  
  252. Um aus dem Tag im jahr wieder das Datum zu erhalten, kann die folgende
  253. Routine verwendet werden:
  254.  
  255. FUNCTION YearDayToDMY(GYear,DayNumber : INTEGER; VAR Day,Month,Year : WORD);
  256. CONST
  257.   MonthDays : Array [1..12] of integer=
  258.                     (31,28,31,30,31,30,31,31,30,31,30,31);
  259. VAR
  260.   I    : integer;
  261.   done ,
  262.     ly : boolean;
  263. BEGIN
  264.    I := 1;
  265.    done:=false;
  266.    ly := ((Gyear MOD 4 = 0) AND (Gyear MOD 100 <> 0))
  267.          OR (Gyear MOD 400 = 0);
  268.    IF ly THEN MonthDays[2] := 29; { correct for leap year february }
  269.    REPEAT
  270.       If DayNumber > MonthDays[i] THEN BEGIN
  271.         DayNumber := DayNumber - MonthDays[i];
  272.         Inc(i);
  273.       END{IF} ELSE BEGIN
  274.         year  := GYear;
  275.         month := i;
  276.         day   := DayNumber;
  277.         done  := TRUE;
  278.       END{ELSE};
  279.    UNTIL (i > 12) OR done;
  280.    IF i > 12 THEN BEGIN
  281.      year:=GYear;
  282.      month:=12;
  283.      day:=31;
  284.     END{IF};
  285. END;
  286.  
  287.  
  288. Berechnung des julianischen Datums
  289. ----------------------------------
  290.  
  291. Diese Routinen dienen der Umwandlung des Datums in eine serielle
  292. julianische Zahl im Bereich von 01.01.1900 bis zum 31.12.2078,
  293. wobei 0 fuer den 01.01.1900 steht (uebringens: 1900 war kein Schalt-
  294. jahr und der 01.01. war ein Montag).
  295.  
  296. FUNCTION DateOk(day,month,year : WORD) : BOOLEAN;
  297. VAR
  298.    ly,ok : BOOLEAN;
  299.   maxday : WORD;
  300. BEGIN
  301.   ok := (year >= 1900) AND (year <= 2078);
  302.   ly := ((year MOD 4 = 0) AND (year MOD 100 <> 0)) OR (year MOD 400 = 0);
  303.   IF ok THEN
  304.     ok := (month >= 01) AND (month <= 12);
  305.    IF ok THEN BEGIN
  306.      IF month IN [01,03,05,07,08,10,12] THEN
  307.        maxday := 31
  308.      ELSE
  309.        IF month <> 2 THEN
  310.          maxday := 30
  311.        ELSE
  312.          IF ly THEN
  313.            maxday := 29
  314.          ELSE
  315.            maxday := 28;
  316.      ok := (day >= 01) AND (day <= maxday);
  317.    END{IF};
  318.    DateOK := ok;
  319. END{FUNC};
  320.  
  321. FUNCTION DMYtoDate(day,month,year : WORD) : WORD;
  322. VAR
  323.   jul : Word;
  324. BEGIN
  325.    IF NOT DateOK(day,month,year) THEN BEGIN
  326.      DMYToDate := $FFFF { signal an invalid date }
  327.    END{IF} ELSE BEGIN  { convert back to DMY }
  328.     IF (Year = 1900) AND (Month < 3) THEN
  329.       IF Month = 1 THEN
  330.         jul := Pred(Day)
  331.       ELSE
  332.         jul := Day + 30
  333.     ELSE BEGIN
  334.       IF Month > 2 THEN
  335.         Dec (Month,3)
  336.       ELSE BEGIN
  337.         Inc (Month,9);
  338.         Dec (Year);
  339.       END{ELSE};
  340.       Dec(year,1900);
  341.       jul := ((1461 * LONGINT(Year)) div 4) +
  342.              ((153 * Month+2) div 5) + Day + 58;
  343.     END{ELSE};
  344.   END{ELSE};
  345.   DMYToDate := jul;
  346. END;
  347.  
  348. PROCEDURE DateToDMY(jul : WORD; VAR day,month,year: WORD);
  349. VAR
  350.   LongTemp ,
  351.   Temp     : LONGINT;
  352. BEGIN
  353.    IF jul <= 58 THEN BEGIN
  354.      year := 1900;
  355.      IF jul <= 31 THEN BEGIN
  356.        month := 1;
  357.        day := Succ(jul);
  358.      END ELSE BEGIN
  359.        month := 2;
  360.        day := jul - 30;
  361.      END{ELSE}
  362.    END{IF} ELSE BEGIN
  363.      IF jul < $FF63 THEN BEGIN
  364.        LongTemp := (4 * LONGINT(jul-58)) - 1;
  365.        year := LongTemp DIV 1461;
  366.        temp := ((LongTemp MOD 1461) DIV 4) * 5 + 2;
  367.        month := temp DIV 153;
  368.        day := ((temp MOD 153) + 5) DIV 5;
  369.        Inc(year,1900);
  370.        IF month < 10 THEN
  371.          Inc(month,3)
  372.        ELSE BEGIN
  373.          Dec(month,9);
  374.          Inc(year);
  375.        END{ELSE};
  376.      END{IF} ELSE BEGIN  { error in date range }
  377.        year := 0;
  378.        month := 0;
  379.        day := 0;
  380.      END{ELSE};
  381.    END{ELSE};
  382. END;
  383.